package com.yh.csx.event.store.config;

import com.yh.csx.bsf.core.util.JsonUtils;
import com.yh.csx.event.store.gateway.log.LogGateway;
import com.yh.csx.event.store.gateway.log.dataobject.OperateLogDo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * @author zhangsheng
 * @date 2021/8/23 16:06
 */
@Slf4j
@Aspect
@Component
public class LogAspect {
    public static final String CLASS_SUFIX_METHOD = ".";

    @Autowired
    private LogGateway logGateway;

    @Pointcut("execution(public * com.yh.csx.event.store.gateway.*.*(..))")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        long startTime = System.currentTimeMillis();
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        String execMethod = pjp.getTarget().getClass().getSimpleName() + CLASS_SUFIX_METHOD + signature.getName();
        log.info("=around=>method:{} ", execMethod);
        try {
            LogMsg logMsg = pjp.getTarget().getClass().getMethod(signature.getName(),
                    signature.getParameterTypes()).getAnnotation(LogMsg.class);
            Object result = pjp.proceed();
            if (null != logMsg) {
                OperateLogDo operateLogDo = new OperateLogDo();
                operateLogDo.setRefId("0");
                operateLogDo.setLogType(execMethod);
                operateLogDo.setCreateTime(new Date());
                if (logMsg.logResult()) {
                    operateLogDo.setContext(JsonUtils.serialize(new Context(getParam(pjp), result)));
                } else {
                    operateLogDo.setContext(JsonUtils.serialize(new Context(getParam(pjp), null)));
                }
                logGateway.log(operateLogDo);
                log.info("<=around=>method:{} ,spend seconds:{} ,log:{}", operateLogDo.getLogType(),
                        (System.currentTimeMillis() - startTime) / 1000, operateLogDo);
            }
            return result;
        } catch (Exception e) {
            log.warn("<=around=>exec {} ,error:", execMethod, e);
            throw e;
        }

    }

    public Map<String, Object> getParam(ProceedingJoinPoint proceedingJoinPoint) {
        Map<String, Object> map = new HashMap<String, Object>();
        Object[] values = proceedingJoinPoint.getArgs();
        String[] names = ((CodeSignature) proceedingJoinPoint.getSignature()).getParameterNames();
        for (int i = 0; i < names.length; i++) {
            map.put(names[i], values[i]);
        }
        return map;
    }

    @Data
    @AllArgsConstructor
    @Accessors(chain = true)
    private class Context {
        private Map<String, Object> params;
        private Object result;
    }
}
